package com.demo.controller;

import cn.hutool.captcha.AbstractCaptcha;
import cn.hutool.captcha.CaptchaUtil;
import cn.hutool.captcha.generator.RandomGenerator;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.extra.qrcode.QrCodeUtil;
import cn.hutool.extra.qrcode.QrConfig;
import com.demo.common.ResultData;
import com.demo.enums.CaptchaTypeEnum;
import com.demo.filter.RequestContextHelper;
import com.demo.util.RedisUniqueStringGenerator;
import com.demo.util.RedisUtil;
import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.awt.*;
import java.io.IOException;
import java.util.HashMap;
import java.util.concurrent.TimeUnit;

/**
 * 二维码和图形验证码
 *
 * @author wangfengchen
 */
@Slf4j
@RestController
@RequestMapping("/code")
public class CodeController {

    @Autowired
    private RedisUtil redisUtil;

    private static final String host = "http://jueduiwanmei.com";
    private static final String localPath = "/Users/wangfengchen/Downloads/test/qrcode.jpg";

    private static final String logo = "/Users/wangfengchen/Downloads/test/1.png";


    /**
     * 生成指定url对应的二维码到文件，宽和高都是300像素，可以跳转到网站
     *
     * @return
     */
    @GetMapping("/generateQrCodeLocal")
    public ResultData<Void> generateQrCodeLocal() {
        QrCodeUtil.generate(host, 300, 300, FileUtil.file(localPath));
        return ResultData.success();
    }

    /**
     * 自定义参数
     *
     * @return
     */
    @GetMapping("/generateQrCodeLocalCustom")
    public ResultData<Void> generateQrCodeLocalCustom() {
        QrConfig config = QrConfig.create()
                .setWidth(300)
                .setHeight(300)
                // 设置边距，即二维码和背景之间的边距
                .setMargin(3)
                // 设置前景色，即二维码颜色（青色）
                .setForeColor(Color.CYAN)
                // 设置背景色（灰色）
                .setBackColor(Color.GRAY)
                // 调整纠错级别，很多时候，二维码无法识别，这时就要调整纠错级别。纠错级别使用zxing的ErrorCorrectionLevel枚举封装，包括：L、M、Q、H几个参数，由低到高。低级别的像素块更大，可以远距离识别，但是遮挡就会造成无法识别。高级别则相反，像素块小，允许遮挡一定范围，但是像素块更密集。
                .setErrorCorrection(ErrorCorrectionLevel.H)
                // 二维码中间附带logo
                .setImg(logo);
        // 生成二维码到文件，也可以到流
        QrCodeUtil.generate(host, config, FileUtil.file(localPath));
        return ResultData.success();
    }

    /**
     * 识别二维码
     *
     * @return
     */
    @GetMapping("/decodeQrCode")
    public ResultData<Object> decodeQrCode() {
        String decode = QrCodeUtil.decode(FileUtil.file(localPath));
        return ResultData.success(decode);
    }

    /**
     * 生成SVG矢量图二维码
     *
     * @return
     */
    @GetMapping("/generateQrCodeSvg")
    public ResultData<Object> generateQrCodeSvg() {
        QrConfig config = QrConfig.create().setImg(logo)
                .setForeColor(Color.blue)
                .setBackColor(Color.pink)
                .setRatio(8)
                .setErrorCorrection(ErrorCorrectionLevel.M)
                .setMargin(1);
        String svg = QrCodeUtil.generateAsSvg(host, config);
        return ResultData.success(svg);
    }

    /**
     * 生成Ascii Art字符画二维码
     *
     * @return
     */
    @GetMapping("/generateQrCodeAscii")
    public ResultData<Object> generateQrCodeAscii() {
        QrConfig config = QrConfig.create()
                .setForeColor(Color.BLUE)
                .setBackColor(Color.MAGENTA)
                .setWidth(0)
                .setHeight(0).setMargin(1);
        String asciiArt = QrCodeUtil.generateAsAsciiArt(host, config);
        System.out.println(asciiArt);
        return ResultData.success(asciiArt);
    }

    /**
     * 生成二维码Base64
     * 转换地址：<a href="https://onlinetools.com/image/convert-base64-to-image">https://onlinetools.com/image/convert-base64-to-image</a>
     *
     * @return
     */
    @GetMapping("/generateQrCodeBase64")
    public ResultData<Object> generateQrCodeBase64() {
        QrConfig config = QrConfig.create()
                .setWidth(300)
                .setHeight(300)
                // 设置边距，即二维码和背景之间的边距ø
                .setMargin(3)
                // 设置前景色，即二维码颜色（青色）
                .setForeColor(Color.CYAN)
                // 设置背景色（灰色）
                .setBackColor(Color.GRAY)
                // 调整纠错级别，很多时候，二维码无法识别，这时就要调整纠错级别。纠错级别使用zxing的ErrorCorrectionLevel枚举封装，包括：L、M、Q、H几个参数，由低到高。低级别的像素块更大，可以远距离识别，但是遮挡就会造成无法识别。高级别则相反，像素块小，允许遮挡一定范围，但是像素块更密集。
//        .setErrorCorrection(ErrorCorrectionLevel.H)
                // 二维码中间附带logo
                .setImg(logo);
        String jpeg = QrCodeUtil.generateAsBase64(host, config, "jpeg");
        return ResultData.success(jpeg);
    }

    /**
     * 生成图形验证码
     *
     * @param type
     * @return
     */
    @GetMapping("/generateCaptchaCode")
    public ResultData<Object> generateCaptchaCode(@RequestParam(required = false) String type) {
        AbstractCaptcha captcha = null;
        CaptchaTypeEnum captchaTypeEnum;
        if (StringUtils.isBlank(type)) {
            // 如果不传则随机
            captchaTypeEnum = CaptchaTypeEnum.values()[RandomUtil.randomInt(0, 3)];
        } else {
            captchaTypeEnum = CaptchaTypeEnum.match(type);
            if (captchaTypeEnum == null) {
                return ResultData.fail();
            }
        }
        switch (captchaTypeEnum) {
            case CIRCLE:
                captcha = CaptchaUtil.createCircleCaptcha(200, 88, 4, 10);
                break;
            case GIF:
                captcha = CaptchaUtil.createGifCaptcha(200, 88, 4);
                break;
            case LINE:
                captcha = CaptchaUtil.createLineCaptcha(200, 88, 4, 100);
                break;
            case SHEAR:
                // 定义图形验证码的长、宽、验证码字符数、干扰线宽度
                captcha = CaptchaUtil.createShearCaptcha(200, 88, 4, 5);
                break;
            default:
                break;
        }
        HashMap<Object, Object> map = new HashMap<>();
        String code = captcha.getCode();
        map.put("code", code);
        map.put("imageBase64Data", captcha.getImageBase64Data());
        redisUtil.setEx(code, code, 1L, TimeUnit.MINUTES);
        log.info("requestId = {}，type = {}，code = {}", RequestContextHelper.get().getRequestId(), captchaTypeEnum.getName(), code);
        return ResultData.success(map);
    }

    /**
     * 生成图形验证码，输出到浏览器
     *
     * @param type
     * @return
     */
    @GetMapping("/generateCaptchaCodeOutputStream")
    public void generateCaptchaCodeOutputStream(@RequestParam(required = false) String type, HttpServletResponse response) throws IOException {
        AbstractCaptcha captcha = null;
        CaptchaTypeEnum captchaTypeEnum;
        if (StringUtils.isBlank(type)) {
            // 如果不传则随机
            captchaTypeEnum = CaptchaTypeEnum.values()[RandomUtil.randomInt(0, 3)];
        } else {
            captchaTypeEnum = CaptchaTypeEnum.match(type);
            if (captchaTypeEnum == null) {
                response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
                response.getWriter().write("type错误");
                return;
            }
        }
        switch (captchaTypeEnum) {
            case CIRCLE:
                captcha = CaptchaUtil.createCircleCaptcha(200, 88, 4, 10);
                break;
            case GIF:
                captcha = CaptchaUtil.createGifCaptcha(200, 88, 4);
                break;
            case LINE:
                captcha = CaptchaUtil.createLineCaptcha(200, 88, 4, 100);
                break;
            case SHEAR:
                // 定义图形验证码的长、宽、验证码字符数、干扰线宽度
                captcha = CaptchaUtil.createShearCaptcha(200, 88, 4, 5);
                break;
            default:
                break;
        }
        String code = captcha.getCode();
        redisUtil.setEx(code, code, 1L, TimeUnit.MINUTES);
        log.info("requestId = {}，type = {}，code = {}", RequestContextHelper.get().getRequestId(), captchaTypeEnum.getName(), code);
        ServletOutputStream outputStream = response.getOutputStream();
        captcha.write(outputStream);
        outputStream.close();
    }

    /**
     * 生成图形验证码，输出到浏览器，有时候标准的验证码不满足要求，比如我们希望使用纯字母的验证码、纯数字的验证码、加减乘除的验证码，此时我们就要自定义
     *
     * @param type
     * @return
     */
    @GetMapping("/generateCaptchaCodeOutputStreamCustom")
    public void generateCaptchaCodeOutputStreamCustom(@RequestParam(required = false) String type, HttpServletResponse response) throws IOException {
        AbstractCaptcha captcha = null;
        CaptchaTypeEnum captchaTypeEnum;
        if (StringUtils.isBlank(type)) {
            // 如果不传则随机
            captchaTypeEnum = CaptchaTypeEnum.values()[RandomUtil.randomInt(0, 3)];
        } else {
            captchaTypeEnum = CaptchaTypeEnum.match(type);
            if (captchaTypeEnum == null) {
                response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
                response.getWriter().write("type错误");
                return;
            }
        }
        switch (captchaTypeEnum) {
            case CIRCLE:
                captcha = CaptchaUtil.createCircleCaptcha(200, 88, 4, 10);
                break;
            case GIF:
                captcha = CaptchaUtil.createGifCaptcha(200, 88, 4);
                break;
            case LINE:
                captcha = CaptchaUtil.createLineCaptcha(200, 88, 4, 100);
                break;
            case SHEAR:
                // 定义图形验证码的长、宽、验证码字符数、干扰线宽度
                captcha = CaptchaUtil.createShearCaptcha(200, 88, 4, 5);
                break;
            default:
                break;
        }
        // 自定义纯数字的验证码（随机4位数字，可重复）
        RandomGenerator randomGenerator = new RandomGenerator("0123456789", 4);
        captcha.setGenerator(randomGenerator);
        String code = captcha.getCode();
        redisUtil.setEx(code, code, 1L, TimeUnit.MINUTES);
        log.info("requestId = {}，type = {}，code = {}", RequestContextHelper.get().getRequestId(), captchaTypeEnum.getName(), code);
        ServletOutputStream outputStream = response.getOutputStream();
        captcha.write(outputStream);
        outputStream.close();
    }

    /**
     * 验证图形验证码
     *
     * @return
     */
    @GetMapping("/verifyCaptchaCode")
    public ResultData<Object> verifyCaptchaCode(@RequestParam String code) {
        Boolean hasKey = redisUtil.hasKey(code);
        return ResultData.success(hasKey);
    }

    @Autowired
    private RedisUniqueStringGenerator redisUniqueStringGenerator;

    /**
     * redis+lua生成唯一字符串
     * @return
     */
    @GetMapping("/redisUniqueStringGenerator")
    public ResultData<?> redisUniqueStringGenerator() {
        String code = redisUniqueStringGenerator.generateWithRetry("mpc:mgt:inviteCode:", 5);
        return ResultData.success(code);
    }


}
